home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / tde3.zip / ED.C < prev    next >
C/C++ Source or Header  |  1993-06-05  |  69KB  |  2,271 lines

  1. /*******************  start of original comments  ********************/
  2. /*
  3.  * Written by Douglas Thomson (1989/1990)
  4.  *
  5.  * This source code is released into the public domain.
  6.  */
  7.  
  8. /*
  9.  * Name:    dte - Doug's Text Editor program - main editor module
  10.  * Purpose: This file contains the main editor module, and a number of the
  11.  *           smaller miscellaneous editing commands.
  12.  *          It also contains the code for dispatching commands.
  13.  * File:    ed.c
  14.  * Author:  Douglas Thomson
  15.  * System:  this file is intended to be system-independent
  16.  * Date:    October 1, 1989
  17.  * I/O:     file being edited
  18.  *          files read or written
  19.  *          user commands and prompts
  20.  * Notes:   see the file "dte.doc" for general program documentation
  21.  */
  22. /*********************  end of original comments   ********************/
  23.  
  24. /*
  25.  * The basic editor routines have been EXTENSIVELY rewritten.  I have added
  26.  * support for lines longer than 80 columns and I have added line number
  27.  * support.  I like to know the real line number that editor functions are
  28.  * working on and I like to know the total number of lines in a file.
  29.  *
  30.  * I rewrote the big series of ifs in the dispatch subroutine.  It is now
  31.  * an array of pointers to functions.  We know what function to call as soon
  32.  * as a key is pressed.  It is also makes it easier to implement a configuration
  33.  * utility and macros.
  34.  *
  35.  * I added a few functions that I use quite often and I deleted a few that I
  36.  * rarely use.  Added are Split Line, Join Line, and Duplicate Line.  Deleted
  37.  * are Goto Marker 0-9 (others?).
  38.  *
  39.  * ************ In TDE 1.3, I put Goto Marker 0-9 back in.  ***************
  40.  *
  41.  * I felt that the insert routine should be separated into two routines.  One
  42.  * for inserting the various combinations of newlines and one for inserting
  43.  * ASCII and extended ASCII characters.
  44.  *
  45.  * One of Doug's design considerations was keeping screen updates to a minimum.
  46.  * I have expanded upon that idea and added support for updating windows
  47.  * LOCALly, GLOBALly, or NOT_LOCALly.  For example, scrolling in one window
  48.  * does not affect the text in another window - LOCAL update.  Adding, deleting,
  49.  * or modifying text in one window may affect text in other windows - GLOBAL
  50.  * update.  Sometimes, updates to the current window are handled in the task
  51.  * routines so updates to other windows are done NOT_LOCALly.
  52.  *
  53.  * In version 2.2, the big text buffer scheme was replaced by a double
  54.  *  linked list.
  55.  *
  56.  * New editor name:  TDE, the Thomson-Davis Editor.
  57.  * Author:           Frank Davis
  58.  * Date:             June 5, 1991, version 1.0
  59.  * Date:             July 29, 1991, version 1.1
  60.  * Date:             October 5, 1991, version 1.2
  61.  * Date:             January 20, 1992, version 1.3
  62.  * Date:             February 17, 1992, version 1.4
  63.  * Date:             April 1, 1992, version 1.5
  64.  * Date:             June 5, 1992, version 2.0
  65.  * Date:             October 31, 1992, version 2.1
  66.  * Date:             April 1, 1993, version 2.2
  67.  * Date:             June 5, 1993, version 3.0
  68.  *
  69.  * This modification of Douglas Thomson's code is released into the
  70.  * public domain, Frank Davis.   You may distribute it freely.
  71.  */
  72.  
  73. #include "tdestr.h"     /* typedefs for global variables */
  74. #include "define.h"     /* editor function defs */
  75. #include "tdefunc.h"    /* prototypes for all functions in tde */
  76. #include "global.h"     /* global variables */
  77. #include "prompts.h"    /* prompt assignments */
  78. #include "default.h"    /* default function key assignments */
  79.  
  80.  
  81. /*
  82.  * Name:    insert_newline
  83.  * Purpose: insert a newline
  84.  * Date:    June 5, 1991
  85.  * Passed:  window:  pointer to current window
  86.  * Notes:   There a several ways to insert a line into a file:  1) pressing
  87.  *          a key, 2) word wrap, 3) any others?
  88.  *          When doing word wrap or format paragraph, don't show any changes.
  89.  *            Wait until the function finishes then show all changes at once.
  90.  */
  91. int  insert_newline( WINDOW *window )
  92. {
  93. char *source;           /* source for block move to make room for c */
  94. char *dest;             /* destination for block move */
  95. int  len;               /* length of current line */
  96. int  split_len;
  97. int  add;               /* characters to be added (usually 1 in insert mode) */
  98. int  rcol;
  99. int  rc;
  100. long length;
  101. int  carriage_return;
  102. int  split_line;
  103. int  wordwrap;
  104. int  dirty;
  105. int  old_bcol;
  106. register WINDOW *win;   /* put window pointer in a register */
  107. file_infos *file;       /* pointer to file structure in current window */
  108. line_list_ptr new_node;
  109. text_ptr new_line;      /* new line */
  110.  
  111.    rc = OK;
  112.    win = window;
  113.    file = win->file_info;
  114.    length = file->length;
  115.    wordwrap = mode.word_wrap;
  116.    switch (g_status.command) {
  117.       case WordWrap :
  118.          carriage_return = TRUE;
  119.          split_line = FALSE;
  120.          break;
  121.       case AddLine  :
  122.          split_line = carriage_return = FALSE;
  123.          break;
  124.       case SplitLine :
  125.          split_line = carriage_return = TRUE;
  126.          break;
  127.       case Rturn :
  128.       default    :
  129.  
  130.          /*
  131.           * if file is opened in BINARY mode, lets keep the user from
  132.           *   unintentially inserting a line feed into the text.
  133.           */
  134.          if (file->crlf == BINARY)
  135.             return( next_line( win ) );
  136.  
  137.          show_ruler_char( win );
  138.          carriage_return = TRUE;
  139.          split_line = FALSE;
  140.          break;
  141.    }
  142.  
  143.    /*
  144.     * make window temporarily invisible to the un_copy_line function
  145.     */
  146.    new_node = (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  147.    new_line = NULL;
  148.    win->visible = FALSE;
  149.    old_bcol = win->bcol;
  150.    if (rc == OK) {
  151.       new_node->line  = new_line;
  152.       new_node->len   = 0;
  153.       new_node->dirty = FALSE;
  154.  
  155.       if (win->ll->len != EOF) {
  156.          win->file_info->modified = TRUE;
  157.          if (mode.do_backups == TRUE)
  158.             rc = backup_file( win );
  159.          copy_line( win->ll );
  160.          detab_linebuff( );
  161.          len = g_status.line_buff_len;
  162.          split_len = 0;
  163.          if (win->rcol < len)
  164.             win->ll->dirty = TRUE;
  165.  
  166.          source = g_status.line_buff + len;
  167.          if (carriage_return || split_line) {
  168.             if (win->rcol < len) {
  169.                source = g_status.line_buff + win->rcol;
  170.                split_len = len - win->rcol;
  171.                len = win->rcol;
  172.             }
  173.          }
  174.          g_status.line_buff_len = len;
  175.          entab_linebuff( );
  176.          if (un_copy_line( win->ll, win, TRUE ) == OK) {
  177.  
  178.             assert( split_len >= 0 );
  179.             assert( split_len < MAX_LINE_LENGTH );
  180.  
  181.             memmove( g_status.line_buff, source, split_len );
  182.             g_status.line_buff_len = len = split_len;
  183.             g_status.copied = TRUE;
  184.             entab_linebuff( );
  185.          } else
  186.             rc = ERROR;
  187.       } else {
  188.          g_status.line_buff_len = len = 0;
  189.          g_status.copied = TRUE;
  190.       }
  191.  
  192.       if (rc == OK) {
  193.          new_node->line  = new_line;
  194.          new_node->len   = 0;
  195.          new_node->dirty = TRUE;
  196.  
  197.          /*
  198.           * we are somewhere in the list and we need to insert the new node.
  199.           *  if we are anywhere except the EOF node, insert the new node
  200.           *  after the current node.  if the current node is the EOF node,
  201.           *  insert the new node before the EOF node.  this keeps the
  202.           *  EOF node at the end of the list.
  203.           */
  204.          if (win->ll->next != NULL) {
  205.             win->ll->next->prev = new_node;
  206.             new_node->next = win->ll->next;
  207.             win->ll->next = new_node;
  208.             new_node->prev = win->ll;
  209.          } else {
  210.             new_node->next = win->ll;
  211.             if (win->ll->prev != NULL)
  212.                win->ll->prev->next = new_node;
  213.             new_node->prev = win->ll->prev;
  214.             win->ll->prev = new_node;
  215.             if (new_node->prev == NULL)
  216.                win->file_info->line_list = new_node;
  217.             win->ll = new_node;
  218.          }
  219.  
  220.          ++file->length;
  221.          detab_linebuff( );
  222.          entab_linebuff( );
  223.          rc = un_copy_line( new_node, win, FALSE );
  224.          adjust_windows_cursor( win, 1 );
  225.  
  226.          file->dirty = NOT_LOCAL;
  227.          if (length == 0l || wordwrap || win->cline == win->bottom_line)
  228.             file->dirty = GLOBAL;
  229.          else if (!split_line)
  230.             update_line( win );
  231.  
  232.          /*
  233.           * If the cursor is to move down to the next line, then update
  234.           *  the line and column appropriately.
  235.           */
  236.          if (rc == OK  &&  (carriage_return || split_line)) {
  237.             dirty = file->dirty;
  238.             if (win->cline < win->bottom_line)
  239.                win->cline++;
  240.             win->rline++;
  241.             if (win->ll->next != NULL) {
  242.                win->bin_offset += win->ll->len;
  243.                win->ll = win->ll->next;
  244.             }
  245.             rcol = win->rcol;
  246.             old_bcol = win->bcol;
  247.  
  248.             if (win->ll->next != NULL) {
  249.                if (mode.indent || wordwrap) {
  250.                   /*
  251.                    * autoindentation is required. Match the indentation of
  252.                    *  the first line above that is not blank.
  253.                    */
  254.                   add = find_left_margin( wordwrap == FIXED_WRAP ?
  255.                                           win->ll : win->ll->prev, wordwrap );
  256.  
  257.                   assert( add >= 0 );
  258.                   assert( add < MAX_LINE_LENGTH );
  259.  
  260.                   copy_line( win->ll );
  261.                   detab_linebuff( );
  262.                   len = g_status.line_buff_len;
  263.                   source = g_status.line_buff;
  264.                   if (len + add > MAX_LINE_LENGTH)
  265.                      add = MAX_LINE_LENGTH - len;
  266.                   dest = source + add;
  267.  
  268.                   assert( len >= 0);
  269.                   assert( len < MAX_LINE_LENGTH );
  270.  
  271.                   memmove( dest, source, len );
  272.  
  273.                   /*
  274.                    * now put in the autoindent characters
  275.                    */
  276.  
  277.                   assert( add >= 0 );
  278.                   assert( add < MAX_LINE_LENGTH );
  279.  
  280.                   memset( source, ' ', add );
  281.                   win->rcol = add;
  282.                   g_status.line_buff_len += add;
  283.                   entab_linebuff( );
  284.                   rc = un_copy_line( win->ll, win, TRUE );
  285.                } else
  286.                   win->rcol = 0;
  287.             }
  288.             if (rc == OK  &&  split_line) {
  289.                win->rline--;
  290.                win->ll = win->ll->prev;
  291.                if (win->cline > win->top_line + window->ruler)
  292.                   win->cline--;
  293.                win->rcol = rcol;
  294.             }
  295.             check_virtual_col( win, win->rcol, win->ccol );
  296.             if (dirty == GLOBAL || file->dirty == LOCAL || wordwrap)
  297.                file->dirty = GLOBAL;
  298.             else
  299.                file->dirty = dirty;
  300.          }
  301.       } else {
  302.          if (new_node != NULL)
  303.             my_free( new_node );
  304.       }
  305.    } else {
  306.       if (new_node != NULL)
  307.          my_free( new_node );
  308.       error( WARNING, window->bottom_line, main4 );
  309.    }
  310.  
  311.    /*
  312.     * record that file has been modified
  313.     */
  314.    win->visible = TRUE;
  315.    if (rc == OK) {
  316.       if (file->dirty != GLOBAL)
  317.          my_scroll_down( win );
  318.       restore_marked_block( win, 1 );
  319.       show_size( win );
  320.       show_avail_mem( );
  321.       if (old_bcol != win->bcol) {
  322.          make_ruler( win );
  323.          show_ruler( win );
  324.       }
  325.    }
  326.    return( rc );
  327. }
  328.  
  329.  
  330. /*
  331.  * Name:    insert_overwrite
  332.  * Purpose: To make the necessary changes after the user has typed a normal
  333.  *           printable character
  334.  * Date:    June 5, 1991
  335.  * Passed:  window:  pointer to current window
  336.  */
  337. int  insert_overwrite( WINDOW *window )
  338. {
  339. char *source;           /* source for block move to make room for c */
  340. char *dest;             /* destination for block move */
  341. int  len;               /* length of current line */
  342. int  pad;               /* padding to add if cursor beyond end of line */
  343. int  add;               /* characters to be added (usually 1 in insert mode) */
  344. register int rcol;
  345. register WINDOW *win;  /* put window pointer in a register */
  346. int  rc;
  347.  
  348.    win = window;
  349.    if (win->ll->len == EOF || g_status.key_pressed >= 256)
  350.       rc = OK;
  351.    else {
  352.       rcol = win->rcol;
  353.       /*
  354.        * first check we have room - the editor can not
  355.        *  cope with lines wider than g_display.line_length
  356.        */
  357.       if (rcol >= g_display.line_length) {
  358.          /*
  359.           * cannot insert more characters
  360.           */
  361.          error( WARNING, win->bottom_line, ed2 );
  362.          rc = ERROR;
  363.       } else {
  364.          copy_line( win->ll );
  365.          detab_linebuff( );
  366.  
  367.          /*
  368.           * work out how many characters need to be inserted
  369.           */
  370.          len = g_status.line_buff_len;
  371.          pad = rcol > len ? rcol - len : 0;
  372.  
  373.          if (mode.insert || rcol >= len)
  374.             /*
  375.              * inserted characters, or overwritten characters at the end of
  376.              *  the line, are inserted.
  377.              */
  378.             add = 1;
  379.          else
  380.             /*
  381.              *  and no extra space is required to overwrite existing characters
  382.              */
  383.             add = 0;
  384.  
  385.          /*
  386.           * check that current line would not get too long.
  387.           */
  388.          if (len + pad + add >= g_display.line_length) {
  389.             /*
  390.              * no more room to add
  391.              */
  392.             error( WARNING, win->bottom_line, ed3 );
  393.             rc = ERROR;
  394.          } else {
  395.  
  396.             /*
  397.              * make room for whatever needs to be inserted
  398.              */
  399.             if (pad > 0  || add > 0) {
  400.                source = g_status.line_buff + rcol - pad;
  401.                dest = source + pad + add;
  402.  
  403.                assert( len + pad - rcol >= 0 );
  404.                assert( len + pad - rcol < MAX_LINE_LENGTH );
  405.  
  406.                memmove( dest, source, len + pad - rcol );
  407.  
  408.                /*
  409.                 * put in the required padding
  410.                 */
  411.  
  412.                assert( pad >= 0 );
  413.                assert( pad < MAX_LINE_LENGTH );
  414.  
  415.                memset( source, ' ', pad );
  416.             }
  417.             g_status.line_buff[rcol] = (char)g_status.key_pressed;
  418.             g_status.line_buff_len += pad + add;
  419.             entab_linebuff( );
  420.  
  421.             /*
  422.              * always increment the real column (rcol) then adjust the
  423.              * logical and base column as needed.   show the changed line
  424.              * in all but the LOCAL window.  In the LOCAL window, there are
  425.              * two cases:  1) update the line, or 2) redraw the window if
  426.              * cursor goes too far right.
  427.              */
  428.             win->file_info->dirty = NOT_LOCAL;
  429.             win->ll->dirty = TRUE;
  430.             show_changed_line( win );
  431.             if (win->ccol < win->end_col) {
  432.                show_curl_line( win );
  433.                show_ruler_char( win );
  434.                win->ccol++;
  435.             } else {
  436.                win->bcol++;
  437.                win->file_info->dirty = LOCAL;
  438.                make_ruler( win );
  439.                show_ruler( win );
  440.             }
  441.             rcol++;
  442.          }
  443.  
  444.          /*
  445.           * record that file has been modified and adjust cursors,
  446.           * file start and end pointers as needed.
  447.           */
  448.          check_virtual_col( win, rcol, win->ccol );
  449.          win->file_info->modified = TRUE;
  450.          if (mode.word_wrap) {
  451.             add = mode.right_justify;
  452.             mode.right_justify = FALSE;
  453.             g_status.command = FormatText;
  454.             word_wrap( win );
  455.             mode.right_justify = add;
  456.          }
  457.          rc = OK;
  458.       }
  459.    }
  460.    return( rc );
  461. }
  462.  
  463.  
  464. /*
  465.  * Name:    join_line
  466.  * Purpose: To join current line and line below at cursor
  467.  * Date:    June 5, 1991
  468.  * Passed:  window:  pointer to current window
  469.  * Notes:   trunc the line.  then, join with line below, if it exists.
  470.  */
  471. int  join_line( WINDOW *window )
  472. {
  473. int  len;               /* length of current line */
  474. int  new_len;           /* length of the joined lines */
  475. int  next_len;          /* length of the line below current line */
  476. text_ptr q;             /* next line in file */
  477. text_ptr tab_free;      /* next line in file -- with the tabs removed */
  478. int  pad;               /* padding spaces required */
  479. register WINDOW *win;   /* put window pointer in a register */
  480. WINDOW *wp;
  481. line_list_ptr next_node;
  482. int  rc;
  483.  
  484.    win = window;
  485.    if (win->ll->len == EOF  ||  win->ll->next->len == EOF)
  486.       return( ERROR );
  487.  
  488.    rc = OK;
  489.  
  490.    assert( win->ll->next != NULL );
  491.  
  492.    next_node = win->ll->next;
  493.    load_undo_buffer( win->file_info, win->ll->line, win->ll->len );
  494.    copy_line( win->ll );
  495.    detab_linebuff( );
  496.  
  497.    /*
  498.     * if cursor is in line before eol, reset len to rcol
  499.     */
  500.    if (win->rcol < (len = g_status.line_buff_len))
  501.       len = win->rcol;
  502.  
  503.    /*
  504.     * calculate needed padding
  505.     */
  506.    pad = win->rcol > len ? win->rcol - len : 0;
  507.  
  508.    assert( pad >= 0 );
  509.    assert( pad < MAX_LINE_LENGTH );
  510.  
  511.    /*
  512.     * if there any tabs in the next line, expand them because we
  513.     *   probably have to redo them anyway.
  514.     */
  515.    next_len = next_node->len;
  516.    tab_free = detab_a_line( next_node->line, &next_len );
  517.  
  518.    assert( next_len >= 0 );
  519.    assert( next_len < MAX_LINE_LENGTH );
  520.    assert( len >= 0 );
  521.    assert( len < MAX_LINE_LENGTH );
  522.  
  523.    /*
  524.     * check room to combine lines
  525.     */
  526.    new_len = len + pad + next_len;
  527.    if (new_len >= g_display.line_length) {
  528.       /*
  529.        * cannot combine lines.
  530.        */
  531.       error( WARNING, win->bottom_line, ed4 );
  532.       rc = ERROR;
  533.    } else {
  534.       if (mode.do_backups == TRUE) {
  535.          win->file_info->modified = TRUE;
  536.          rc = backup_file( win );
  537.       }
  538.       q = (text_ptr)(g_status.line_buff + len);
  539.       /*
  540.        * insert padding
  541.        */
  542.       if (pad > 0) {
  543.          while (pad--)
  544.             *q++ = ' ';
  545.       }
  546.       _fmemcpy( q, tab_free, next_len );
  547.       g_status.line_buff_len = new_len;
  548.       entab_linebuff( );
  549.  
  550.       if ((rc = un_copy_line( win->ll, win, FALSE )) == OK) {
  551.  
  552.          if (next_node->next != NULL)
  553.             next_node->next->prev = win->ll;
  554.          win->ll->next = next_node->next;
  555.          win->ll->dirty = TRUE;
  556.  
  557.          --win->file_info->length;
  558.          ++win->rline;
  559.          adjust_windows_cursor( win, -1 );
  560.          restore_marked_block( win, -1 );
  561.          --win->rline;
  562.  
  563.          wp = g_status.window_list;
  564.          while (wp != NULL) {
  565.             if (wp->file_info == win->file_info) {
  566.                /*
  567.                 * make sure none of the window pointers point to the
  568.                 *   node we are about to delete.
  569.                 */
  570.                if (wp != win) {
  571.                   if (wp->ll == next_node)
  572.                      wp->ll = win->ll->next;
  573.                }
  574.             }
  575.             wp = wp->next;
  576.          }
  577.  
  578.          /*
  579.           * now, it's safe to delete the next_node line as well as
  580.           *   the next node.
  581.           */
  582.          if (next_node->line != NULL)
  583.             my_free( next_node->line );
  584.          my_free( next_node );
  585.  
  586.          show_size( win );
  587.          show_avail_mem( );
  588.          win->file_info->dirty = GLOBAL;
  589.       }
  590.    }
  591.    return( rc );
  592. }
  593.  
  594.  
  595. /*
  596.  * Name:    word_delete
  597.  * Purpose: To delete from the cursor to the start of the next word.
  598.  * Date:    September 1, 1991
  599.  * Passed:  window:  pointer to current window
  600.  * Notes:   If the cursor is at the right of the line, then combine the
  601.  *           current line with the next one, leaving the cursor where it
  602.  *           is.
  603.  *          If the cursor is on an alphanumeric character, then all
  604.  *           subsequent alphanumeric characters are deleted.
  605.  *          If the cursor is on a space, then all subsequent spaces
  606.  *           are deleted.
  607.  *          If the cursor is on a punctuation character, then all
  608.  *           subsequent punctuation characters are deleted.
  609.  */
  610. int  word_delete( WINDOW *window )
  611. {
  612. int  len;               /* length of current line */
  613. int  count;             /* number of characters deleted from line */
  614. register int start;     /* column that next word starts in */
  615. char *source;           /* source for block move to delete word */
  616. char *dest;             /* destination for block move */
  617. text_ptr p;
  618. register WINDOW *win;   /* put window pointer in a register */
  619. int  rc;
  620.  
  621.    win = window;
  622.    if (win->rline > win->file_info->length  || win->ll->len == EOF)
  623.       return( ERROR );
  624.  
  625.    rc = OK;
  626.    copy_line( win->ll );
  627.    detab_linebuff( );
  628.    if (win->rcol >= (len = g_status.line_buff_len)) {
  629.       rc = join_line( win );
  630.       if (rc == OK) {
  631.          p = win->ll->line;
  632.          if (p != NULL) {
  633.             p += win->rcol;
  634.             if (win->rcol < win->ll->len) {
  635.                len = win->ll->len - win->rcol;
  636.                load_undo_buffer( win->file_info, p, len );
  637.             }
  638.          }
  639.       }
  640.    } else {
  641.  
  642.       assert( len >= 0);
  643.       assert( len < MAX_LINE_LENGTH );
  644.  
  645.       /*
  646.        * normal word delete
  647.        *
  648.        * find the start of the next word
  649.        */
  650.       start = win->rcol;
  651.       if (isspace( g_status.line_buff[start] )) {
  652.          /*
  653.           * the cursor was on a space, so eat all consecutive spaces
  654.           *  from the cursor onwards.
  655.           */
  656.          while (start < len  &&  isspace( g_status.line_buff[start] ))
  657.             ++start;
  658.       } else {
  659.          /*
  660.           * eat all consecutive characters in the same class (spaces
  661.           *  are considered to be in the same class as the cursor
  662.           *  character)
  663.           */
  664.          while (start < len  &&  !isspace( g_status.line_buff[start] ))
  665.             ++start;
  666.          while (start < len  &&  isspace( g_status.line_buff[start] ))
  667.             ++start;
  668.       }
  669.  
  670.       /*
  671.        * move text to delete word
  672.        */
  673.       count = start - win->rcol;
  674.       source = g_status.line_buff + start;
  675.       dest = g_status.line_buff + win->rcol;
  676.  
  677.       assert( len - start >= 0 );
  678.  
  679.       memmove( dest, source, len - start );
  680.       g_status.line_buff_len = len - count;
  681.       entab_linebuff( );
  682.       win->file_info->modified = TRUE;
  683.       win->file_info->dirty = GLOBAL;
  684.       win->ll->dirty = TRUE;
  685.  
  686.       /*
  687.        * word_delete is also called by the word processing functions to get
  688.        *   rid of spaces.
  689.        */
  690.       if (g_status.command == WordDelete)
  691.          show_changed_line( win );
  692.    }
  693.    return( rc );
  694. }
  695.  
  696.  
  697. /*
  698.  * Name:    dup_line
  699.  * Purpose: Duplicate current line
  700.  * Date:    June 5, 1991
  701.  * Passed:  window:  pointer to current window
  702.  * Notes:   cursor stays on current line
  703.  */
  704. int  dup_line( WINDOW *window )
  705. {
  706. register int len;       /* length of current line */
  707. text_ptr p;
  708. register WINDOW *win;   /* put window pointer in a register */
  709. line_list_ptr next_node;
  710. int  rc;
  711.  
  712.    win = window;
  713.  
  714.    /*
  715.     * don't dup a NULL line
  716.     */
  717.    if (win->rline > win->file_info->length  ||  win->ll->len == EOF)
  718.       return( ERROR );
  719.  
  720.    entab_linebuff( );
  721.    rc = un_copy_line( win->ll, win, TRUE );
  722.    len = win->ll->len;
  723.  
  724.    assert( len >= 0);
  725.    assert( len < MAX_LINE_LENGTH );
  726.  
  727.    p = NULL;
  728.    next_node = NULL;
  729.    if (rc == OK) {
  730.       p = (text_ptr)my_malloc( len, &rc );
  731.       next_node = (line_list_ptr)my_malloc( sizeof(line_list_struc), &rc );
  732.    }
  733.  
  734.    if (rc == OK) {
  735.       win->file_info->modified = TRUE;
  736.       if (mode.do_backups == TRUE)
  737.          rc = backup_file( win );
  738.       ++win->file_info->length;
  739.  
  740.       if (len > 0)
  741.          _fmemcpy( p, win->ll->line, len );
  742.  
  743.       next_node->line  = p;
  744.       next_node->dirty = TRUE;
  745.       next_node->len   = len;
  746.  
  747.       if (win->ll->next != NULL)
  748.          win->ll->next->prev = next_node;
  749.  
  750.       next_node->next = win->ll->next;
  751.       next_node->prev = win->ll;
  752.       win->ll->next = next_node;
  753.  
  754.       adjust_windows_cursor( win, 1 );
  755.  
  756.       /*
  757.        * if current line is the bottom line, we can't see the dup line because
  758.        * cursor doesn't move and dup line is added after current line.
  759.        */
  760.       if  (win->cline != win->bottom_line)
  761.          my_scroll_down( win );
  762.       win->file_info->dirty = NOT_LOCAL;
  763.  
  764.       /*
  765.        * record that file has been modified
  766.        */
  767.       restore_marked_block( win, 1 );
  768.       show_size( win );
  769.       show_avail_mem( );
  770.    } else {
  771.       /*
  772.        * cannot duplicate line
  773.        */
  774.       if (p != NULL)
  775.          my_free( p );
  776.       if (next_node != NULL)
  777.          my_free( next_node );
  778.       error( WARNING, win->bottom_line, ed5 );
  779.    }
  780.    return( rc );
  781. }
  782.  
  783.  
  784. /*
  785.  * Name:    back_space
  786.  * Purpose: To delete the character to the left of the cursor.
  787.  * Date:    June 5, 1991
  788.  * Passed:  window:  pointer to current window
  789.  * Notes:   If the cursor is at the left of the line, then combine the
  790.  *           current line with the previous one.
  791.  *          If in indent mode, and the cursor is on the first non-blank
  792.  *           character of the line, then match the indentation of an
  793.  *           earlier line.
  794.  */
  795. int  back_space( WINDOW *window )
  796. {
  797. int  rc;                /* return code */
  798. int  len;               /* length of the current line */
  799. char *source;           /* source of block move to delete character */
  800. char *dest;             /* destination of block move */
  801. text_ptr p;             /* previous line in file */
  802. int  plen;              /* length of previous line */
  803. int  del_count;         /* number of characters to delete */
  804. int  pos;               /* the position of the first non-blank char */
  805. register int rcol;
  806. int  ccol;
  807. int  old_bcol;
  808. register WINDOW *win;  /* put window pointer in a register */
  809. WINDOW *wp;
  810. line_list_ptr temp_ll;
  811.  
  812.    win = window;
  813.    if (win->rline > win->file_info->length || win->ll->len == EOF)
  814.       return( ERROR );
  815.    rc = OK;
  816.    copy_line( win->ll );
  817.    detab_linebuff( );
  818.    len = g_status.line_buff_len;
  819.    rcol = win->rcol;
  820.    ccol = win->ccol;
  821.    old_bcol = win->bcol;
  822.    if (rcol == 0) {
  823.       if (win->rline > 1) {
  824.          /*
  825.           * combine this line with the previous, if any
  826.           */
  827.  
  828.          assert( win->ll->prev != NULL );
  829.  
  830.          p = win->ll->prev->line;
  831.          plen = win->ll->prev->len;
  832.          if (len + 2 + plen >= g_display.line_length) {
  833.             /*
  834.              * cannot combine lines
  835.              */
  836.             error( WARNING, win->bottom_line, ed4 );
  837.             return( ERROR );
  838.          }
  839.  
  840.          win->file_info->modified = TRUE;
  841.          if ((rc = un_copy_line( win->ll, win, TRUE )) == OK) {
  842.             --win->rline;
  843.             win->ll = win->ll->prev;
  844.             win->bin_offset -= win->ll->len;
  845.             win->ll->dirty = TRUE;
  846.             copy_line( win->ll );
  847.             detab_linebuff( );
  848.             len = g_status.line_buff_len;
  849.             rcol = len;
  850.  
  851.             p = win->ll->next->line;
  852.             plen = win->ll->next->len;
  853.  
  854.             /*
  855.              * copy previous line into new previous line.
  856.              */
  857.             assert( plen >= 0 );
  858.             assert( len  >= 0 );
  859.  
  860.             _fmemcpy( g_status.line_buff+len, p, plen );
  861.             g_status.line_buff_len = len + plen;
  862.  
  863.             load_undo_buffer( win->file_info, p, plen );
  864.             if (p != NULL)
  865.                my_free( p );
  866.  
  867.             temp_ll = win->ll->next;
  868.  
  869.             if (temp_ll->prev != NULL)
  870.                temp_ll->prev->next = temp_ll->next;
  871.             temp_ll->next->prev = temp_ll->prev;
  872.  
  873.             --win->file_info->length;
  874.             ++win->rline;
  875.             restore_marked_block( win, -1 );
  876.             adjust_windows_cursor( win, -1 );
  877.             --win->rline;
  878.  
  879.             wp = g_status.window_list;
  880.             while (wp != NULL) {
  881.                if (wp->file_info == win->file_info) {
  882.                   if (wp != win) {
  883.                      if (wp->ll == temp_ll)
  884.                         wp->ll = win->ll->next;
  885.                   }
  886.                }
  887.                wp = wp->next;
  888.             }
  889.  
  890.             my_free( temp_ll );
  891.  
  892.             if (win->cline > win->top_line + win->ruler)
  893.                --win->cline;
  894.  
  895.             /*
  896.              * make sure cursor stays on the screen, at the end of the
  897.              *  previous line
  898.              */
  899.             ccol = rcol - win->bcol;
  900.             show_size( win );
  901.             show_avail_mem( );
  902.             check_virtual_col( win, rcol, ccol );
  903.             win->file_info->dirty = GLOBAL;
  904.             make_ruler( win );
  905.             show_ruler( win );
  906.          }
  907.       } else
  908.          return( ERROR );
  909.    } else {
  910.       /*
  911.        * normal delete
  912.        *
  913.        * find out how much to delete (depends on indent mode)
  914.        */
  915.       del_count = 1;   /* the default */
  916.       if (mode.indent) {
  917.          /*
  918.           * indent only happens if the cursor is on the first
  919.           *  non-blank character of the line
  920.           */
  921.          pos = first_non_blank( (text_ptr)g_status.line_buff, len );
  922.          if (pos == rcol  ||
  923.                          is_line_blank( (text_ptr)g_status.line_buff, len )) {
  924.             /*
  925.              * now work out how much to indent
  926.              */
  927.             temp_ll = win->ll->prev;
  928.             for (; temp_ll != NULL; temp_ll=temp_ll->prev) {
  929.                p = temp_ll->line;
  930.                plen = first_non_blank( p, temp_ll->len );
  931.                if (plen < rcol  &&  plen != temp_ll->len) {
  932.                   /*
  933.                    * found the line to match
  934.                    */
  935.                   del_count = rcol - plen;
  936.                   break;
  937.                }
  938.             }
  939.          }
  940.       }
  941.  
  942.       /*
  943.        * move text to delete char(s), unless no chars actually there
  944.        */
  945.       if (rcol - del_count < len) {
  946.          dest = g_status.line_buff + rcol - del_count;
  947.          if (rcol > len) {
  948.             source = g_status.line_buff + len;
  949.             pos = 0;
  950.             len = (rcol + 1) - del_count;
  951.          } else {
  952.             source = g_status.line_buff + rcol;
  953.             pos = len - rcol;
  954.             len = len - del_count;
  955.          }
  956.  
  957.          assert( pos >= 0 );
  958.          assert( len >= 0 );
  959.          assert( len <= MAX_LINE_LENGTH );
  960.  
  961.          memmove( dest, source, pos );
  962.          g_status.line_buff_len = len;
  963.          entab_linebuff( );
  964.       }
  965.       rcol -= del_count;
  966.       ccol -= del_count;
  967.       win->file_info->dirty = NOT_LOCAL;
  968.       win->ll->dirty = TRUE;
  969.       show_ruler_char( win );
  970.       show_changed_line( win );
  971.       check_virtual_col( win, rcol, ccol );
  972.       if (!win->file_info->dirty)
  973.          show_curl_line( win );
  974.       if (old_bcol != win->bcol) {
  975.          make_ruler( win );
  976.          show_ruler( win );
  977.       }
  978.    }
  979.    win->file_info->modified = TRUE;
  980.    return( rc );
  981. }
  982.  
  983.  
  984. /*
  985.  * Name:    line_kill
  986.  * Purpose: To delete the line the cursor is on.
  987.  * Date:    June 5, 1991
  988.  * Passed:  window:  pointer to current window
  989.  * Notes:   win->ll->s == NULL then do not do a
  990.  *          line kill (can't kill a NULL line).
  991.  */
  992. int  line_kill( WINDOW *window )
  993. {
  994. register WINDOW *win;   /* put window pointer in a register */
  995. register WINDOW *wp;
  996. line_list_ptr killed_node;
  997. int  rc;
  998.  
  999.    win = window;
  1000.    killed_node = win->ll;
  1001.    rc = OK;
  1002.    if (killed_node->len != EOF) {
  1003.       win->file_info->modified = TRUE;
  1004.       if (mode.do_backups == TRUE)
  1005.          rc = backup_file( win );
  1006.  
  1007.       if (rc == OK) {
  1008.          load_undo_buffer( win->file_info,
  1009.             g_status.copied ? (text_ptr)g_status.line_buff : killed_node->line,
  1010.             g_status.copied ? g_status.line_buff_len       : killed_node->len );
  1011.  
  1012.          --win->file_info->length;
  1013.  
  1014.          win->ll = win->ll->next;
  1015.  
  1016.          if (killed_node->prev != NULL)
  1017.             killed_node->prev->next = killed_node->next;
  1018.          else
  1019.             win->file_info->line_list = win->ll;
  1020.  
  1021.          killed_node->next->prev = killed_node->prev;
  1022.  
  1023.          wp = g_status.window_list;
  1024.          while (wp != NULL) {
  1025.             if (wp->file_info == win->file_info) {
  1026.                if (wp != win) {
  1027.                   if (wp->ll == killed_node)
  1028.                      wp->ll = win->ll;
  1029.                }
  1030.             }
  1031.             wp = wp->next;
  1032.          }
  1033.  
  1034.          /*
  1035.           * free the line and the node
  1036.           */
  1037.          if (killed_node->line != NULL)
  1038.             my_free( killed_node->line );
  1039.          my_free( killed_node );
  1040.  
  1041.          win->file_info->dirty = NOT_LOCAL;
  1042.  
  1043.          g_status.copied = FALSE;
  1044.          /*
  1045.           * move all cursors one according to i, restore begin and end block
  1046.           */
  1047.          adjust_windows_cursor( win, -1 );
  1048.          restore_marked_block( win, -1 );
  1049.  
  1050.          /*
  1051.           * we are not doing a GLOBAL update, so update current window here
  1052.           */
  1053.          if (win->file_info->dirty == NOT_LOCAL)
  1054.             my_scroll_down( win );
  1055.          show_size( win );
  1056.          show_avail_mem( );
  1057.       }
  1058.    } else
  1059.       rc = ERROR;
  1060.    return( rc );
  1061. }
  1062.  
  1063.  
  1064. /*
  1065.  * Name:    char_del_under
  1066.  * Purpose: To delete the character under the cursor.
  1067.  * Date:    June 5, 1991
  1068.  * Passed:  window:  pointer to current window
  1069.  * Notes:   If the cursor is beyond the end of the line, then this
  1070.  *           command is ignored.
  1071.  *          DeleteChar and StreamDeleteChar use this function.
  1072.  */
  1073. int  char_del_under( WINDOW *window )
  1074. {
  1075. char *source;    /* source of block move to delete character */
  1076. int  len;
  1077. register WINDOW *win;   /* put window pointer in a register */
  1078.  
  1079.    win = window;
  1080.    if (win->rline > win->file_info->length || win->ll->len == EOF)
  1081.       return( OK );
  1082.    copy_line( win->ll );
  1083.    detab_linebuff( );
  1084.    if (win->rcol < (len = g_status.line_buff_len)) {
  1085.       /*
  1086.        * move text to delete using buffer
  1087.        */
  1088.       source = g_status.line_buff + win->rcol + 1;
  1089.  
  1090.       assert( len - win->rcol >= 0 );
  1091.  
  1092.       memmove( source-1, source, len - win->rcol );
  1093.       --g_status.line_buff_len;
  1094.       entab_linebuff( );
  1095.       win->file_info->dirty    = GLOBAL;
  1096.       win->file_info->modified = TRUE;
  1097.       win->ll->dirty = TRUE;
  1098.       show_changed_line( win );
  1099.    } else if (g_status.command == StreamDeleteChar)
  1100.       join_line( win );
  1101.    return( OK );
  1102. }
  1103.  
  1104.  
  1105. /*
  1106.  * Name:    eol_kill
  1107.  * Purpose: To delete everything from the cursor to the end of the line.
  1108.  * Date:    June 5, 1991
  1109.  * Passed:  window:  pointer to current window
  1110.  * Notes:   If the cursor is beyond the end of the line, then this
  1111.  *           command is ignored.
  1112.  */
  1113. int  eol_kill( WINDOW *window )
  1114. {
  1115. register WINDOW *win;   /* put window pointer in a register */
  1116.  
  1117.    win = window;
  1118.    if (win->rline > win->file_info->length  ||  win->ll->len == EOF)
  1119.       return( OK );
  1120.    copy_line( win->ll );
  1121.    detab_linebuff( );
  1122.    load_undo_buffer( win->file_info, (text_ptr)g_status.line_buff,
  1123.                      g_status.line_buff_len );
  1124.    if (win->rcol < g_status.line_buff_len) {
  1125.       /*
  1126.        * truncate to delete rest of line
  1127.        */
  1128.       g_status.line_buff_len = win->rcol;
  1129.       entab_linebuff( );
  1130.       win->file_info->dirty = GLOBAL;
  1131.       win->ll->dirty = TRUE;
  1132.       show_changed_line( win );
  1133.    }
  1134.    return( OK );
  1135. }
  1136.  
  1137.  
  1138. /*
  1139.  * Name:    undo_line
  1140.  * Purpose: To retrieve unaltered line if possible.
  1141.  * Date:    June 5, 1991
  1142.  * Passed:  window:  pointer to current window
  1143.  * Notes:   Changes are made to the line buffer so the underlying text has
  1144.  *          not changed.  Put the unchanged line from the file into the
  1145.  *          line buffer and display it.
  1146.  */
  1147. int  undo_line( WINDOW *window )
  1148. {
  1149. register WINDOW *win;   /* put window pointer in a register */
  1150.  
  1151.    win = window;
  1152.    if (win->rline <= win->file_info->length  &&  win->ll->len != EOF &&
  1153.                             g_status.copied) {
  1154.       g_status.copied = FALSE;
  1155.       copy_line( win->ll );
  1156.       detab_linebuff( );
  1157.       win->file_info->dirty = GLOBAL;
  1158.       show_changed_line( win );
  1159.    }
  1160.    return( OK );
  1161. }
  1162.  
  1163.  
  1164. /*
  1165.  * Name:    undo
  1166.  * Purpose: To retrieve (pop) a line from the undo stack
  1167.  * Date:    September 26, 1991
  1168.  * Passed:  window:  pointer to current window
  1169.  * Notes:   Insert an empty line into the file then pop the line in the undo
  1170.  *          stack.  When we pop line 0, there are no more lines on the stack.
  1171.  *          Set the stack pointer to -1 to indicate an empty stack.
  1172.  */
  1173. int  undo( WINDOW *window )
  1174. {
  1175. register WINDOW *win;   /* put window pointer in a register */
  1176. line_list_ptr node;
  1177.  
  1178.    win = window;
  1179.    if (win->file_info->undo_count > 0) {
  1180.       entab_linebuff( );
  1181.       if (un_copy_line( win->ll, win, TRUE ) == ERROR)
  1182.          return( ERROR );
  1183.  
  1184.       node = win->file_info->undo_top;
  1185.       win->file_info->undo_top = node->next;
  1186.       win->file_info->undo_top->prev = NULL;
  1187.       --win->file_info->undo_count;
  1188.  
  1189.       node->next = node->prev = NULL;
  1190.  
  1191.       ++win->file_info->length;
  1192.  
  1193.       if (win->ll->prev != NULL)
  1194.          win->ll->prev->next = node;
  1195.       node->prev = win->ll->prev;
  1196.  
  1197.       win->ll->prev = node;
  1198.       node->next = win->ll;
  1199.       win->ll = node;
  1200.       win->ll->dirty = TRUE;
  1201.  
  1202.       if (win->ll->prev == NULL)
  1203.          win->file_info->line_list = win->ll;
  1204.  
  1205.       adjust_windows_cursor( win, 1 );
  1206.  
  1207.       /*
  1208.        * we have now undeleted a line.  increment the file length and display
  1209.         * it.
  1210.        */
  1211.       win->file_info->dirty = GLOBAL;
  1212.       show_size( win );
  1213.       show_avail_mem( );
  1214.    }
  1215.    return( OK );
  1216. }
  1217.  
  1218.  
  1219. /*
  1220.  * Name:    beg_next_line
  1221.  * Purpose: To move the cursor to the beginning of the next line.
  1222.  * Date:    October 4, 1991
  1223.  * Passed:  window:  pointer to current window
  1224.  */
  1225. int  beg_next_line( WINDOW *window )
  1226. {
  1227. int  rc;
  1228.  
  1229.    window->rcol = 0;
  1230.    rc = prepare_move_down( window );
  1231.    check_virtual_col( window, window->rcol, window->ccol );
  1232.    sync( window );
  1233.    make_ruler( window );
  1234.    show_ruler( window );
  1235.    return( rc );
  1236. }
  1237.  
  1238.  
  1239. /*
  1240.  * Name:    next_line
  1241.  * Purpose: To move the cursor to the first character of the next line.
  1242.  * Date:    October 4, 1991
  1243.  * Passed:  window:  pointer to current window
  1244.  */
  1245. int  next_line( WINDOW *window )
  1246. {
  1247. register int rcol;
  1248. register WINDOW *win;   /* put window pointer in a register */
  1249. int  rc;
  1250.  
  1251.    win = window;
  1252.    rc = prepare_move_down( win );
  1253.    rcol = first_non_blank( win->ll->line, win->ll->len );
  1254.    check_virtual_col( win, rcol, win->ccol );
  1255.    sync( win );
  1256.    make_ruler( win );
  1257.    show_ruler( win );
  1258.    return( rc );
  1259. }
  1260.  
  1261.  
  1262. /*
  1263.  * Name:    home
  1264.  * Purpose: To move the cursor to the left of the current line.
  1265.  * Date:    June 5, 1991
  1266.  * Passed:  window:  pointer to current window
  1267.  * Notes:   this routine is made a little more complicated with cursor sync.
  1268.  *            if the g_status.copied flag is set we need to see from what file
  1269.  *            the line_buff was copied.
  1270.  */
  1271. int  home( WINDOW *window )
  1272. {
  1273. register int rcol;
  1274. register WINDOW *win;   /* put window pointer in a register */
  1275. text_ptr p;
  1276.  
  1277.    win = window;
  1278.    if (g_status.copied && win->file_info == g_status.current_window->file_info){
  1279.       rcol = first_non_blank( (text_ptr)g_status.line_buff,
  1280.                                         g_status.line_buff_len );
  1281.       if (is_line_blank( (text_ptr)g_status.line_buff, g_status.line_buff_len))
  1282.          rcol = 0;
  1283.    } else {
  1284.       p = win->ll->line;
  1285.       if (p == NULL)
  1286.          rcol = 0;
  1287.       else {
  1288.          rcol = first_non_blank( p, win->ll->len );
  1289.          if (is_line_blank( p, win->ll->len ))
  1290.             rcol = 0;
  1291.       }
  1292.    }
  1293.    if (win->rcol == rcol)
  1294.       rcol = 0;
  1295.    check_virtual_col( win, rcol, win->ccol );
  1296.    sync( win );
  1297.    make_ruler( win );
  1298.    show_ruler( win );
  1299.    return( OK );
  1300. }
  1301.  
  1302.  
  1303. /*
  1304.  * Name:    goto_eol
  1305.  * Purpose: To move the cursor to the eol character of the current line.
  1306.  * Date:    June 5, 1991
  1307.  * Passed:  window:  pointer to current window
  1308.  * Notes:   this routine is made a little more complicated with cursor sync.
  1309.  *            if the g_status.copied flag is set we need to see from what file
  1310.  *            the line_buff was copied.
  1311.  */
  1312. int  goto_eol( WINDOW *window )
  1313. {
  1314. register int rcol;
  1315. register WINDOW *win;   /* put window pointer in a register */
  1316.  
  1317.    win = window;
  1318.    rcol = find_end( win->ll->line, win->ll->len );
  1319.    if (g_status.copied) {
  1320.       if (win->file_info == g_status.current_window->file_info)
  1321.          rcol = find_end( (text_ptr)g_status.line_buff, g_status.line_buff_len);
  1322.    }
  1323.    win->ccol = win->start_col + rcol - win->bcol;
  1324.    check_virtual_col( win, rcol, win->ccol );
  1325.    sync( win );
  1326.    make_ruler( win );
  1327.    show_ruler( win );
  1328.    return( OK );
  1329. }
  1330.  
  1331.  
  1332. /*
  1333.  * Name:    goto_top
  1334.  * Purpose: To move the cursor to the top of the current window.
  1335.  * Date:    June 5, 1991
  1336.  * Passed:  window:  pointer to current window
  1337.  * Notes:   If the start of the file occurs before the top of the window,
  1338.  *           then the start of the file is moved to the top of the window.
  1339.  */
  1340. int  goto_top( WINDOW *window )
  1341. {
  1342. register WINDOW *win;   /* put window pointer in a register */
  1343.  
  1344.    win = window;
  1345.    entab_linebuff( );
  1346.    if (un_copy_line( win->ll, win, TRUE ) == ERROR)
  1347.       return( ERROR );
  1348.    update_line( win );
  1349.    for (; win->cline > win->top_line+win->ruler; win->cline--,win->rline--) {
  1350.       if (win->rline <= 1L)
  1351.          break;
  1352.       else {
  1353.          win->ll = win->ll->prev;
  1354.          win->bin_offset -= win->ll->len;
  1355.       }
  1356.    }
  1357.    show_curl_line( win );
  1358.    sync( win );
  1359.    return( OK );
  1360. }
  1361.  
  1362.  
  1363. /*
  1364.  * Name:    goto_bottom
  1365.  * Purpose: To move the cursor to the bottom of the current window.
  1366.  * Date:    June 5, 1991
  1367.  * Passed:  window:  pointer to current window
  1368.  */
  1369. int  goto_bottom( WINDOW *window )
  1370. {
  1371. register WINDOW *win;   /* put window pointer in a register */
  1372. int  at_top;
  1373.  
  1374.    win = window;
  1375.    entab_linebuff( );
  1376.    if (un_copy_line( win->ll, win, TRUE ) == ERROR)
  1377.       return( ERROR );
  1378.    if (win->ll->len == EOF) {
  1379.       if (win->rline > 1) {
  1380.          at_top = FALSE;
  1381.          if (win->cline == win->top_line + win->ruler) {
  1382.             win->file_info->dirty = LOCAL;
  1383.             at_top = TRUE;
  1384.          }
  1385.          if (!at_top)
  1386.             update_line( win );
  1387.          --win->rline;             /* ALWAYS decrement line counter */
  1388.          win->ll = win->ll->prev;
  1389.          win->bin_offset -= win->ll->len;
  1390.          if (!at_top) {
  1391.             --win->cline;          /* we aren't at top of screen - so move up */
  1392.             show_curl_line( win );
  1393.          }
  1394.       }
  1395.    } else {
  1396.       update_line( win );
  1397.       for (; win->cline < win->bottom_line; win->cline++,win->rline++) {
  1398.          if (win->ll == NULL || win->ll->next == NULL || win->ll->next->len == EOF)
  1399.             break;
  1400.          else {
  1401.             win->bin_offset += win->ll->len;
  1402.             win->ll = win->ll->next;
  1403.          }
  1404.       }
  1405.       show_curl_line( win );
  1406.    }
  1407.    sync( win );
  1408.    return( OK );
  1409. }
  1410.  
  1411.  
  1412. /*
  1413.  * Name:    set_tabstop
  1414.  * Purpose: To set the current interval between tab stops
  1415.  * Date:    October 1, 1989
  1416.  * Notes:   Tab interval must be reasonable, and this function will
  1417.  *           not allow tabs more than MAX_COLS / 2.
  1418.  */
  1419. int  set_tabstop( WINDOW *window )
  1420. {
  1421. char num_str[MAX_COLS]; /* tab interval as a character string */
  1422. int  tab;               /* new tab interval */
  1423. register int rc;
  1424. register file_infos *file;
  1425.  
  1426.    itoa( mode.ltab_size, num_str, 10 );
  1427.    /*
  1428.     * tab interval:
  1429.     */
  1430.    rc = get_name( ed7a, window->bottom_line, num_str, g_display.message_color );
  1431.    if (rc == OK   &&  *num_str != '\0') {
  1432.       tab = atoi( num_str );
  1433.       if (tab < MAX_COLS/2) {
  1434.          mode.ltab_size = tab;
  1435.          if (mode.inflate_tabs) {
  1436.             for (file=g_status.file_list; file != NULL; file=file->next)
  1437.                file->dirty = GLOBAL;
  1438.          }
  1439.       } else {
  1440.          /*
  1441.           * tab size too long
  1442.           */
  1443.          error( WARNING, window->bottom_line, ed8 );
  1444.          rc = ERROR;
  1445.       }
  1446.    }
  1447.  
  1448.    itoa( mode.ptab_size, num_str, 10 );
  1449.    /*
  1450.     * tab interval:
  1451.     */
  1452.    rc = get_name( ed7b, window->bottom_line, num_str, g_display.message_color );
  1453.    if (rc == OK  &&  *num_str != '\0') {
  1454.       tab = atoi( num_str );
  1455.       if (tab < MAX_COLS/2) {
  1456.          mode.ptab_size = tab;
  1457.          show_tab_modes( );
  1458.          if (mode.inflate_tabs) {
  1459.             for (file=g_status.file_list; file != NULL; file=file->next)
  1460.                file->dirty = GLOBAL;
  1461.          }
  1462.       } else {
  1463.          /*
  1464.           * tab size too long
  1465.           */
  1466.          error( WARNING, window->bottom_line, ed8 );
  1467.          rc = ERROR;
  1468.       }
  1469.    }
  1470.    return( rc );
  1471. }
  1472.  
  1473.  
  1474. /*
  1475.  * Name:    show_line_col
  1476.  * Purpose: show current real line and column of current cursor position
  1477.  * Date:    June 5, 1991
  1478.  * Passed:  window:  pointer to current window
  1479.  * Notes:   Blank old position and display new position.  current line and
  1480.  *          column may take up to 12 columns, which allows the display of
  1481.  *          9,999 columns and 9,999,999 lines.
  1482.  */
  1483. void show_line_col( WINDOW *window )
  1484. {
  1485. int  i;
  1486. register int k;
  1487. char line_col[20], num[10];
  1488. char *hex_digit = "0123456789abcdef";
  1489.  
  1490.    /*
  1491.     * blank out current line:column position.
  1492.     */
  1493.    memset( line_col, ' ', 13 );
  1494.    line_col[13] = '\0';
  1495.  
  1496.    /*
  1497.     * convert column to ascii and store in display buffer.
  1498.     */
  1499.    itoa( window->rcol+1, num, 10 );
  1500.    i = strlen( num ) - 1;
  1501.    for (k=12; i>=0; i--, k--)
  1502.       line_col[k] = num[i];
  1503.  
  1504.    /*
  1505.     * put in colon to separate line and column
  1506.     */
  1507.    line_col[k--] = ':';
  1508.  
  1509.    /*
  1510.     * convert line to ascii and store in display buffer.
  1511.     */
  1512.    ltoa( window->rline, num, 10 );
  1513.    i = strlen( num ) - 1;
  1514.    for (; i>=0; i--, k--)
  1515.       line_col[k] = num[i];
  1516.  
  1517.    /*
  1518.     * find line to start line:column display then output
  1519.     */
  1520.    s_output( line_col, window->top_line-1, window->end_col-12,
  1521.              g_display.head_color );
  1522.  
  1523.    strcpy( line_col, " =   " );
  1524.    i = window->rcol;
  1525.    if (g_status.copied) {
  1526.       if (i < g_status.line_buff_len) {
  1527.          k = (int)g_status.line_buff[i];
  1528.          line_col[2] = *(hex_digit + (k >> 4));
  1529.          line_col[3] = *(hex_digit + (k & 0x000f));
  1530.          line_col[4] = 'x';
  1531.          i = TRUE;
  1532.       } else
  1533.          i = FALSE;
  1534.    } else {
  1535.       if (i < window->ll->len) {
  1536.          k = (int)window->ll->line[i];
  1537.          line_col[2] = *(hex_digit + (k >> 4));
  1538.          line_col[3] = *(hex_digit + (k & 0x000f));
  1539.          line_col[4] = 'x';
  1540.          i = TRUE;
  1541.       } else
  1542.          i = FALSE;
  1543.    }
  1544.    s_output( line_col, g_display.mode_line, 58, g_display.mode_color );
  1545.    if (i == TRUE)
  1546.       c_output( k, 58, g_display.mode_line, g_display.mode_color );
  1547.  
  1548.  
  1549.    /*
  1550.     * if file was opened in binary mode, show offset from beginning of file.
  1551.     */
  1552.    if (window->file_info->crlf == BINARY && !window->vertical) {
  1553.       k =  window->ll->line == NULL  ?  0  :  window->rcol;
  1554.       memset( line_col, ' ', 7 );
  1555.       line_col[7] = '\0';
  1556.       s_output( line_col, window->top_line-1, 61, g_display.head_color );
  1557.       ltoa( window->bin_offset + k, line_col, 10 );
  1558.       s_output( line_col, window->top_line-1, 61, g_display.head_color );
  1559.    }
  1560.    show_asterisk( window );
  1561. }
  1562.  
  1563.  
  1564. /*
  1565.  * Name:    show_asterisk
  1566.  * Purpose: give user an indication if file is dirty
  1567.  * Date:    September 16, 1991
  1568.  * Passed:  window:  pointer to current window
  1569.  */
  1570. void show_asterisk( WINDOW *window )
  1571. {
  1572.    c_output( window->file_info->modified ? '*' : ' ', window->start_col+4,
  1573.              window->top_line-1, g_display.head_color );
  1574. }
  1575.  
  1576.  
  1577. /*
  1578.  * Name:    toggle_overwrite
  1579.  * Purpose: toggle overwrite-insert mode
  1580.  * Date:    September 16, 1991
  1581.  * Passed:  arg_filler:  argument to satify function prototype
  1582.  */
  1583. int  toggle_overwrite( WINDOW *arg_filler )
  1584. {
  1585.    mode.insert = !mode.insert;
  1586.    show_insert_mode( );
  1587.    set_cursor_size( mode.insert ? g_display.insert_cursor :
  1588.                     g_display.overw_cursor );
  1589.    return( OK );
  1590. }
  1591.  
  1592.  
  1593. /*
  1594.  * Name:    toggle_smart_tabs
  1595.  * Purpose: toggle smart tab mode
  1596.  * Date:    June 5, 1992
  1597.  * Passed:  arg_filler:  argument to satify function prototype
  1598.  */
  1599. int  toggle_smart_tabs( WINDOW *arg_filler )
  1600. {
  1601.    mode.smart_tab = !mode.smart_tab;
  1602.    show_tab_modes( );
  1603.    return( OK );
  1604. }
  1605.  
  1606.  
  1607. /*
  1608.  * Name:    toggle_indent
  1609.  * Purpose: toggle indent mode
  1610.  * Date:    September 16, 1991
  1611.  * Passed:  arg_filler:  argument to satify function prototype
  1612.  */
  1613. int  toggle_indent( WINDOW *arg_filler )
  1614. {
  1615.    mode.indent = !mode.indent;
  1616.    show_indent_mode( );
  1617.    return( OK );
  1618. }
  1619.  
  1620.  
  1621. /*
  1622.  * Name:    set_left_margin
  1623.  * Purpose: set left margin for word wrap
  1624.  * Date:    November 27, 1991
  1625.  * Passed:  window
  1626.  */
  1627. int  set_left_margin( WINDOW *window )
  1628. {
  1629. register int rc;
  1630. char temp[MAX_COLS];
  1631.  
  1632.    itoa( mode.left_margin + 1, temp, 10 );
  1633.    /*
  1634.     * enter left margin
  1635.     */
  1636.    rc = get_name( ed9, window->bottom_line, temp, g_display.message_color );
  1637.    if (rc == OK  &&  *temp != '\0') {
  1638.       rc = atoi( temp ) - 1;
  1639.       if (rc < 0 || rc >= mode.right_margin) {
  1640.          /*
  1641.           * left margin out of range
  1642.           */
  1643.          error( WARNING, window->bottom_line, ed10 );
  1644.          rc = ERROR;
  1645.       } else {
  1646.          mode.left_margin = rc;
  1647.          show_all_rulers( );
  1648.       }
  1649.    }
  1650.    return( rc );
  1651. }
  1652.  
  1653.  
  1654. /*
  1655.  * Name:    set_right_margin
  1656.  * Purpose: set right margin for word wrap
  1657.  * Date:    November 27, 1991
  1658.  * Passed:  window
  1659.  */
  1660. int  set_right_margin( WINDOW *window )
  1661. {
  1662. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1663. register int rc;
  1664. int  prompt_line;
  1665. char temp[MAX_COLS];
  1666.  
  1667.    prompt_line = window->bottom_line;
  1668.    save_screen_line( 0, prompt_line, line_buff );
  1669.    set_prompt( ed11a, prompt_line );
  1670.    rc = get_yn( );
  1671.    restore_screen_line( 0, prompt_line, line_buff );
  1672.    if (rc != ERROR) {
  1673.       mode.right_justify =  rc == A_YES ? TRUE : FALSE;
  1674.  
  1675.       itoa( mode.right_margin + 1, temp, 10 );
  1676.       /*
  1677.        * enter right margin
  1678.        */
  1679.       rc = get_name( ed11, prompt_line, temp, g_display.message_color );
  1680.       if (rc == OK  &&  *temp != '\0') {
  1681.          rc = atoi( temp ) - 1;
  1682.          if (rc <= mode.left_margin || rc > MAX_LINE_LENGTH) {
  1683.             /*
  1684.              * right margin out of range
  1685.              */
  1686.             error( WARNING, prompt_line, ed12 );
  1687.             rc = ERROR;
  1688.          } else {
  1689.             mode.right_margin = rc;
  1690.             show_all_rulers( );
  1691.          }
  1692.       }
  1693.    }
  1694.    return( rc );
  1695. }
  1696.  
  1697.  
  1698. /*
  1699.  * Name:    set_paragraph_margin
  1700.  * Purpose: set column to begin paragraph
  1701.  * Date:    November 27, 1991
  1702.  * Passed:  window
  1703.  * Notes:   paragraph may be indented, flush, or offset.
  1704.  */
  1705. int  set_paragraph_margin( WINDOW *window )
  1706. {
  1707. register int rc;
  1708. char temp[80];
  1709.  
  1710.    itoa( mode.parg_margin + 1, temp, 10 );
  1711.    /*
  1712.     * enter paragraph margin
  1713.     */
  1714.    rc = get_name( ed13, window->bottom_line, temp, g_display.message_color );
  1715.    if (rc == OK  &&  *temp != '\0') {
  1716.       rc = atoi( temp ) - 1;
  1717.       if (rc < 0 || rc >= mode.right_margin) {
  1718.          /*
  1719.           * paragraph margin out of range
  1720.           */
  1721.          error( WARNING, window->bottom_line, ed14 );
  1722.          rc = ERROR;
  1723.       } else {
  1724.          mode.parg_margin = rc;
  1725.          show_all_rulers( );
  1726.       }
  1727.    }
  1728.    return( rc );
  1729. }
  1730.  
  1731.  
  1732. /*
  1733.  * Name:    toggle_crlf
  1734.  * Purpose: toggle crlf mode
  1735.  * Date:    November 27, 1991
  1736.  * Passed:  arg_filler:  argument to satify function prototype
  1737.  */
  1738. int  toggle_crlf( WINDOW *window )
  1739. {
  1740. register WINDOW *w;
  1741.  
  1742.    ++window->file_info->crlf;
  1743.    if (window->file_info->crlf > BINARY)
  1744.       window->file_info->crlf = CRLF;
  1745.    w = g_status.window_list;
  1746.    while (w != NULL) {
  1747.       if (w->file_info == window->file_info  &&  w->visible)
  1748.          show_crlf_mode( w );
  1749.       w = w->next;
  1750.    }
  1751.    return( OK );
  1752. }
  1753.  
  1754.  
  1755. /*
  1756.  * Name:    toggle_ww
  1757.  * Purpose: toggle word wrap mode
  1758.  * Date:    November 27, 1991
  1759.  * Passed:  arg_filler:  argument to satify function prototype
  1760.  */
  1761. int  toggle_ww( WINDOW *arg_filler )
  1762. {
  1763.    ++mode.word_wrap;
  1764.    if (mode.word_wrap > DYNAMIC_WRAP)
  1765.       mode.word_wrap = NO_WRAP;
  1766.    show_wordwrap_mode( );
  1767.    return( OK );
  1768. }
  1769.  
  1770.  
  1771. /*
  1772.  * Name:    toggle_trailing
  1773.  * Purpose: toggle eleminating trainling space at eol
  1774.  * Date:    November 25, 1991
  1775.  * Passed:  arg_filler:  argument to satify function prototype
  1776.  */
  1777. int  toggle_trailing( WINDOW *arg_filler )
  1778. {
  1779.    mode.trailing = !mode.trailing;
  1780.    show_trailing( );
  1781.    return( OK );
  1782. }
  1783.  
  1784.  
  1785. /*
  1786.  * Name:    toggle_z
  1787.  * Purpose: toggle writing control z at eof
  1788.  * Date:    November 25, 1991
  1789.  * Passed:  arg_filler:  argument to satify function prototype
  1790.  */
  1791. int  toggle_z( WINDOW *arg_filler )
  1792. {
  1793.    mode.control_z = !mode.control_z;
  1794.    show_control_z( );
  1795.    return( OK );
  1796. }
  1797.  
  1798.  
  1799. /*
  1800.  * Name:    toggle_eol
  1801.  * Purpose: toggle writing eol character at eol
  1802.  * Date:    November 25, 1991
  1803.  * Passed:  arg_filler:  argument to satify function prototype
  1804.  */
  1805. int  toggle_eol( WINDOW *arg_filler )
  1806. {
  1807. register file_infos *file;
  1808.  
  1809.    mode.show_eol = !mode.show_eol;
  1810.    for (file=g_status.file_list; file != NULL; file=file->next)
  1811.       file->dirty = GLOBAL;
  1812.    return( OK );
  1813. }
  1814.  
  1815.  
  1816. /*
  1817.  * Name:    toggle_search_case
  1818.  * Purpose: toggle search case
  1819.  * Date:    September 16, 1991
  1820.  * Passed:  arg_filler:  argument to satify function prototype
  1821.  */
  1822. int  toggle_search_case( WINDOW *arg_filler )
  1823. {
  1824.    mode.search_case = (mode.search_case == IGNORE) ? MATCH : IGNORE;
  1825.    show_search_case( );
  1826.    build_boyer_array( );
  1827.    return( OK );
  1828. }
  1829.  
  1830.  
  1831. /*
  1832.  * Name:    toggle_sync
  1833.  * Purpose: toggle sync mode
  1834.  * Date:    January 15, 1992
  1835.  * Passed:  arg_filler:  argument to satify function prototype
  1836.  */
  1837. int  toggle_sync( WINDOW *arg_filler )
  1838. {
  1839.    mode.sync = !mode.sync;
  1840.    show_sync_mode( );
  1841.    return( OK );
  1842. }
  1843.  
  1844.  
  1845. /*
  1846.  * Name:    toggle_ruler
  1847.  * Purpose: toggle ruler
  1848.  * Date:    March 5, 1992
  1849.  * Passed:  arg_filler:  argument to satify function prototype
  1850.  */
  1851. int  toggle_ruler( WINDOW *arg_filler )
  1852. {
  1853. register WINDOW *wp;
  1854.  
  1855.    mode.ruler = !mode.ruler;
  1856.    wp = g_status.window_list;
  1857.    while (wp != NULL) {
  1858.       if (mode.ruler) {
  1859.          /*
  1860.           * there has to be more than one line in a window to display a ruler.
  1861.           *   even if the ruler mode is on, we need to check the num of lines.
  1862.           */
  1863.          if (wp->bottom_line - wp->top_line >0) {
  1864.             if (wp->cline == wp->top_line)
  1865.                ++wp->cline;
  1866.             if (wp->cline > wp->bottom_line)
  1867.                wp->cline = wp->bottom_line;
  1868.             wp->ruler = TRUE;
  1869.          } else
  1870.             wp->ruler = FALSE;
  1871.       } else {
  1872.  
  1873.          /*
  1874.           * if this is the first page in a file, then we may need to "pull"
  1875.           *   the file up before displaying the first page.
  1876.           */
  1877.          if (wp->rline == ((wp->cline - wp->ruler) - (wp->top_line - 1)))
  1878.             --wp->cline;
  1879.          if (wp->cline < wp->top_line)
  1880.             wp->cline = wp->top_line;
  1881.          wp->ruler = FALSE;
  1882.       }
  1883.       make_ruler( wp );
  1884.       setup_window( wp );
  1885.       if (wp->visible)
  1886.          redraw_current_window( wp );
  1887.       wp = wp->next;
  1888.    }
  1889.    return( OK );
  1890. }
  1891.  
  1892.  
  1893. /*
  1894.  * Name:    toggle_tabinflate
  1895.  * Purpose: toggle inflating tabs
  1896.  * Date:    October 31, 1992
  1897.  * Passed:  arg_filler:  argument to satify function prototype
  1898.  */
  1899. int  toggle_tabinflate( WINDOW *arg_filler )
  1900. {
  1901. register file_infos *file;
  1902.  
  1903.    mode.inflate_tabs = !mode.inflate_tabs;
  1904.    for (file=g_status.file_list; file != NULL; file=file->next)
  1905.       file->dirty = GLOBAL;
  1906.    show_tab_modes( );
  1907.    return( OK );
  1908. }
  1909.  
  1910.  
  1911. /*
  1912.  * Name:    sync
  1913.  * Purpose: carry out cursor movements in all visible windows
  1914.  * Date:    January 15, 1992
  1915.  * Passed:  window
  1916.  * Notes:   switch sync semaphore when we do this so we don't get into a
  1917.  *          recursive loop.  all cursor movement commands un_copy_line before
  1918.  *          moving the cursor off the current line.   you MUST make certain
  1919.  *          that the current line is uncopied in the task routines that
  1920.  *          move the cursor off the current line before calling sync.
  1921.  */
  1922. void sync( WINDOW *window )
  1923. {
  1924. register WINDOW *wp;
  1925. register file_infos *fp;
  1926.  
  1927.    if (mode.sync && mode.sync_sem) {
  1928.  
  1929.    /*
  1930.     * these functions must un_copy a line before sync'ing
  1931.     */
  1932. #if defined( __MSC__ )
  1933.       switch (g_status.command) {
  1934.          case  NextLine        :
  1935.          case  BegNextLine     :
  1936.          case  LineDown        :
  1937.          case  LineUp          :
  1938.          case  WordRight       :
  1939.          case  WordLeft        :
  1940.          case  ScreenDown      :
  1941.          case  ScreenUp        :
  1942.          case  EndOfFile       :
  1943.          case  TopOfFile       :
  1944.          case  BotOfScreen     :
  1945.          case  TopOfScreen     :
  1946.          case  JumpToLine      :
  1947.          case  CenterWindow    :
  1948.          case  CenterLine      :
  1949.          case  ScrollDnLine    :
  1950.          case  ScrollUpLine    :
  1951.          case  PanUp           :
  1952.          case  PanDn           :
  1953.          case  NextDirtyLine   :
  1954.          case  PrevDirtyLine   :
  1955.          case  ParenBalance    :
  1956.             assert( g_status.copied == FALSE );
  1957.             break;
  1958.          default  :
  1959.             break;
  1960.       }
  1961. #endif
  1962.  
  1963.       mode.sync_sem = FALSE;
  1964.       for (wp = g_status.window_list;  wp != NULL;  wp = wp->next) {
  1965.          if (wp->visible  &&  wp != window) {
  1966.  
  1967.             /*
  1968.              * when we sync a command, we need to use the same assertions
  1969.              *  as those in editor( ).
  1970.              *
  1971.              * if everything is everything, these core asserts are TRUE.
  1972.              */
  1973. #if defined( __MSC__ )
  1974.             assert( wp != NULL );
  1975.             assert( wp->file_info != NULL );
  1976.             assert( wp->file_info->line_list != NULL );
  1977.             assert( wp->file_info->line_list_end != NULL );
  1978.             assert( wp->file_info->line_list_end->len == EOF );
  1979.             assert( wp->visible == TRUE );
  1980.             assert( wp->rline >= 0 );
  1981.             assert( wp->rline <= wp->file_info->length + 1 );
  1982.             assert( wp->rcol >= 0 );
  1983.             assert( wp->rcol < MAX_LINE_LENGTH );
  1984.             assert( wp->ccol >= wp->start_col );
  1985.             assert( wp->ccol <= wp->end_col );
  1986.             assert( wp->bcol >= 0 );
  1987.             assert( wp->bcol < MAX_LINE_LENGTH );
  1988.             assert( wp->bcol == wp->rcol-(wp->ccol - wp->start_col) );
  1989.             assert( wp->start_col >= 0 );
  1990.             assert( wp->start_col < wp->end_col );
  1991.             assert( wp->end_col < g_display.ncols );
  1992.             assert( wp->cline >= wp->top_line );
  1993.             assert( wp->cline <= wp->bottom_line );
  1994.             assert( wp->top_line > 0 );
  1995.             assert( wp->top_line <= wp->bottom_line );
  1996.             assert( wp->bottom_line < MAX_LINES );
  1997.             assert( wp->bin_offset >= 0 );
  1998.             if (wp->ll->next == NULL)
  1999.                assert( wp->ll->len == EOF );
  2000.             else
  2001.                assert( wp->ll->len >= 0 );
  2002.             assert( wp->ll->len <  MAX_LINE_LENGTH );
  2003. #endif
  2004.  
  2005.             (*do_it[g_status.command])( wp );
  2006.             show_line_col( wp );
  2007.             show_ruler_pointer( wp );
  2008.          }
  2009.       }
  2010.       mode.sync_sem = TRUE;
  2011.       for (fp = g_status.file_list; fp != NULL; fp = fp->next)
  2012.          if (fp->dirty != FALSE)
  2013.             fp->dirty = GLOBAL;
  2014.    }
  2015. }
  2016.  
  2017.  
  2018. /*
  2019.  * Name:    editor
  2020.  * Purpose: Set up the editor structures and display changes as needed.
  2021.  * Date:    June 5, 1991
  2022.  * Notes:   Master editor routine.
  2023.  */
  2024. void editor( )
  2025. {
  2026. char *name;  /* name of file to start editing */
  2027. register WINDOW *window;        /* current active window */
  2028. int  c;
  2029.  
  2030.    /*
  2031.     * initialize search and seize
  2032.     */
  2033.    g_status.sas_defined = FALSE;
  2034.    for (c=0; c<SAS_P; c++)
  2035.       g_status.sas_arg_pointers[c] = NULL;
  2036.  
  2037.    g_status.file_mode = TEXT;
  2038.    /*
  2039.     * Check that user specified file to edit, if not offer help
  2040.     */
  2041.    if (g_status.argc > 1) {
  2042.       c = *g_status.argv[1];
  2043.       if (c == '/'  ||  c == '-') {
  2044.          c = *(g_status.argv[1] + 1);
  2045.          if (c == 'f'  ||  c == 'g') {
  2046.             /*
  2047.              * with search and seize their has to be at least 4 arg's, e.g.
  2048.              *    tde -f findme *.c
  2049.              */
  2050.             if (g_status.argc >= 4) {
  2051.  
  2052.                assert( strlen( g_status.argv[2] ) < MAX_COLS );
  2053.  
  2054.                if (c == 'f') {
  2055.                   g_status.command = DefineGrep;
  2056.                   strcpy( (char *)sas_bm.pattern, g_status.argv[2] );
  2057.                } else {
  2058.                   g_status.command = DefineRegXGrep;
  2059.                   strcpy( (char *)regx.pattern, g_status.argv[2] );
  2060.                }
  2061.  
  2062.                for (c=3; c <= g_status.argc; c++)
  2063.                   g_status.sas_arg_pointers[c-3] = g_status.argv[c];
  2064.                g_status.sas_argc = g_status.argc - 3;
  2065.                g_status.sas_arg = 0;
  2066.                g_status.sas_argv = g_status.sas_arg_pointers;
  2067.                g_status.sas_found_first = FALSE;
  2068.                if (g_status.command == DefineGrep) {
  2069.                   g_status.sas_defined = TRUE;
  2070.                   g_status.sas_search_type = BOYER_MOORE;
  2071.                   bm.search_defined = sas_bm.search_defined = OK;
  2072.                   build_boyer_array( );
  2073.                   c = OK;
  2074.                } else {
  2075.                   c = build_nfa( );
  2076.                   if (c == OK) {
  2077.                      g_status.sas_defined = TRUE;
  2078.                      g_status.sas_search_type = REG_EXPRESSION;
  2079.                      regx.search_defined = sas_regx.search_defined = OK;
  2080.                   } else
  2081.                      g_status.sas_defined = FALSE;
  2082.                }
  2083.                if (c != ERROR)
  2084.                   c = search_and_seize( g_status.current_window );
  2085.             } else
  2086.                c = ERROR;
  2087.          } else if (c == 'b' || c == 'B') {
  2088.             c = atoi( g_status.argv[1] + 2 );
  2089.             if (c <= 0 || c >= MAX_LINE_LENGTH)
  2090.                c = DEFAULT_BIN_LENGTH;
  2091.             ++g_status.arg;
  2092.             g_status.file_mode = BINARY;
  2093.             g_status.file_chunk = c;
  2094.             c = edit_next_file( g_status.current_window );
  2095.          } else
  2096.             c = ERROR;
  2097.       } else
  2098.          c = edit_next_file( g_status.current_window );
  2099.    } else {
  2100.       name = g_status.rw_name;
  2101.       *name = '\0';
  2102.       /*
  2103.        * file name to edit
  2104.        */
  2105.       c = get_name( ed15, g_display.nlines, name, g_display.text_color );
  2106.  
  2107.       assert( strlen( name ) < MAX_COLS );
  2108.  
  2109.       if (c == OK) {
  2110.          if (*name != '\0')
  2111.             c = attempt_edit_display( name, GLOBAL, TEXT, 0 );
  2112.          else
  2113.             c = dir_help( (WINDOW *)NULL );
  2114.       }
  2115.    }
  2116.  
  2117.    g_status.stop =   c == OK  ?  FALSE  :  TRUE;
  2118.    if (c == OK)
  2119.       set_cursor_size( mode.insert ? g_display.insert_cursor :
  2120.                        g_display.overw_cursor );
  2121.  
  2122.    /*
  2123.     * main loop - keep updating the display and processing any commands
  2124.     *  while user has not pressed the stop key
  2125.     */
  2126.    for (; g_status.stop != TRUE;) {
  2127.       window = g_status.current_window;
  2128.  
  2129.  
  2130.       /*
  2131.        * before we do any editor commands, we start out with some basic
  2132.        *   assumptions.
  2133.        *
  2134.        * if everything is everything, these core asserts are TRUE.
  2135.        */
  2136.       assert( window != NULL );
  2137.       assert( window->file_info != NULL );
  2138.       assert( window->file_info->line_list != NULL );
  2139.       assert( window->file_info->line_list_end != NULL );
  2140.       assert( window->file_info->line_list_end->len == EOF );
  2141.       assert( window->visible == TRUE );
  2142.       assert( window->rline >= 0 );
  2143.       assert( window->rline <= window->file_info->length + 1 );
  2144.       assert( window->rcol >= 0 );
  2145.       assert( window->rcol < MAX_LINE_LENGTH );
  2146.       assert( window->ccol >= window->start_col );
  2147.       assert( window->ccol <= window->end_col );
  2148.       assert( window->bcol >= 0 );
  2149.       assert( window->bcol < MAX_LINE_LENGTH );
  2150.       assert( window->bcol == window->rcol-(window->ccol - window->start_col) );
  2151.       assert( window->start_col >= 0 );
  2152.       assert( window->start_col < window->end_col );
  2153.       assert( window->end_col < g_display.ncols );
  2154.       assert( window->cline >= window->top_line );
  2155.       assert( window->cline <= window->bottom_line );
  2156.       assert( window->top_line > 0 );
  2157.       assert( window->top_line <= window->bottom_line );
  2158.       assert( window->bottom_line < MAX_LINES );
  2159.       assert( window->bin_offset >= 0 );
  2160.       if (window->ll->next == NULL)
  2161.          assert( window->ll->len == EOF );
  2162.       else
  2163.          assert( window->ll->len >= 0 );
  2164.       assert( window->ll->len <  MAX_LINE_LENGTH );
  2165.  
  2166.       display_dirty_windows( window );
  2167.  
  2168.       /*
  2169.        * set the critical error handler flag to a known state before we
  2170.        *   do each editor command.
  2171.        */
  2172.       ceh.flag = OK;
  2173.  
  2174. /*
  2175.  * this code is used during testing to check the amount of memory
  2176.  *    in the near heap.
  2177.  *
  2178.  *  ultoa( _fmsize( window->ll ), buff, 10 );
  2179.  *  s_output( "s=       ", g_display.mode_line, 15, g_display.mode_color );
  2180.  *  s_output( buff, g_display.mode_line, 17, g_display.mode_color );
  2181.  */
  2182.  
  2183.       /*
  2184.        * Get a key from the user.  Look up the function assigned to that key.
  2185.        * All regular text keys are assigned to function 0.  Text characters
  2186.        * are less than 0x100, decimal 256, which includes the ASCII and
  2187.        * extended ASCII character set.
  2188.        */
  2189.       g_status.key_pressed = getkey( );
  2190.       g_status.command = getfunc( g_status.key_pressed );
  2191.       if (g_status.wrapped  ||  g_status.key_pending) {
  2192.          g_status.key_pending = FALSE;
  2193.          g_status.wrapped = FALSE;
  2194.          show_search_message( CLR_SEARCH, g_display.mode_color );
  2195.       }
  2196.       g_status.control_break = FALSE;
  2197.       if (g_status.command >= 0 && g_status.command < NUM_FUNCS) {
  2198.          record_keys( window->bottom_line );
  2199.          (*do_it[g_status.command])( window );
  2200.       }
  2201.    }
  2202.    cls( );
  2203.    xygoto( 0, 0 );
  2204. }
  2205.  
  2206.  
  2207. /*
  2208.  * Name:    display_dirty_windows
  2209.  * Purpose: Set up the editor structures and display changes as needed.
  2210.  * Date:    June 5, 1991
  2211.  * Notes:   Display all windows with dirty files.
  2212.  */
  2213. void display_dirty_windows( WINDOW *window )
  2214. {
  2215. register WINDOW *below;         /* window below current */
  2216. register WINDOW *above;         /* window above current */
  2217. file_infos *file;               /* temporary file structure */
  2218.  
  2219.    /*
  2220.     * update all windows that point to any file that has been changed
  2221.     */
  2222.    above = below = window;
  2223.    while (above->prev || below->next) {
  2224.       if (above->prev) {
  2225.          above = above->prev;
  2226.          show_dirty_window( above );
  2227.       }
  2228.       if (below->next) {
  2229.          below = below->next;
  2230.          show_dirty_window( below );
  2231.       }
  2232.    }
  2233.    file = window->file_info;
  2234.    if (file->dirty == LOCAL || file->dirty == GLOBAL)
  2235.       display_current_window( window );
  2236.    for (file=g_status.file_list; file != NULL; file=file->next)
  2237.       file->dirty = FALSE;
  2238.  
  2239.    /*
  2240.     * Set the cursor position at window->ccol, window->cline.  Show the
  2241.     * user where in the file the cursor is positioned.
  2242.     */
  2243.    xygoto( window->ccol, window->cline );
  2244.    show_line_col( window );
  2245.    show_ruler_pointer( window );
  2246. }
  2247.  
  2248.  
  2249.  
  2250. /*
  2251.  * Name:    show_dirty_window
  2252.  * Purpose: show changes in non-current window
  2253.  * Date:    June 5, 1991
  2254.  * Passed:  window:  pointer to current window
  2255.  */
  2256. void show_dirty_window( WINDOW *window )
  2257. {
  2258. register WINDOW *win;   /* register window pointer */
  2259. int  dirty;
  2260.  
  2261.   win = window;
  2262.   if (win->visible) {
  2263.      dirty = win->file_info->dirty;
  2264.      if (dirty == GLOBAL || dirty == NOT_LOCAL) {
  2265.         display_current_window( win );
  2266.         show_size( win );
  2267.      }
  2268.      show_asterisk( win );
  2269.   }
  2270. }
  2271.